/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package monkify;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;

/**
 *
 * @author pierre
 */
public class Monkey implements Serializable {
  public static final long serialVersionUID = 0;

  static final String CACHE_PREFIX = "cache_";

  final String id, oid;
  // Position
  int x, y, tx, ty;
  // Toplists
  int topDecade;
  Set<String> topTracks = new HashSet<String>(), 
              topAlbums = new HashSet<String>(), 
              topArtists = new HashSet<String>(), 
              dislikedArtists = new HashSet<String>();
  // URI dictionary
  Map<String, Track> knownURIs = new HashMap<String, Track>();
  // Tracks
  List<Track> unknownTracks;
  List<Track> knownTracks;
  int turn, turnLimit;
  int width, height;
  int remainingCapacity, remainingExecutionTime, boostCooldown;
  int totalPoints = 0, carryingPoints = 0;
  Track foundTrack = null;
  String[][] level;
  Queue<Character> path = new LinkedList<Character>();

  Monkey(String id) {
    this.id = id;
    oid = id.equals("M1") ? "M2" : "M1";
  }

  void init(Scanner sc) {
    parseInit(sc);
    parseToplists(sc);
  }

  void turn(Scanner sc) {
    parseTurn(sc);
    parseMetadata(sc);
    parseLevel(sc);
    
    if(totalPoints > 0 && carryingPoints == 0) {
        //sumo();
    } else if(carryingPoints > 0) {
        //deliver();
    } else if(totalPoints == 0 && foundTrack == null) {
        find();
    } else {
        //gather();
    }
  }
  
  void sumo() {
      LinkedList<Character> theirPath = Maze.getShortestPath(Util.pierreLevel(level, oid, "U"));
      char dir = theirPath.removeLast();
      
      //TODO: finish this
  }
  
  void find() {
      String output = null;
      for(int i = 0; i < width; i++) {
          for(int j = 0; j < height; j++) {
              if(level[i][j].startsWith("s")) {
                  output = level[i][j];
                  break;
              }
          }          
          if(output != null) {
              break;
          }
      }
      
      System.out.print(output);
  }
  
 /* void gather() {
      if(path.isEmpty()) {
          //path = shortestPath(currentPosition, foundTrack);
      } else {
          moveMonkey(path.poll());
          
          if(path.isEmpty()) {
              carryingPoints += foundTrack.value;
          }
      }
  }*/

  void parseInit(Scanner sc) {
    width = sc.nextInt();
    height = sc.nextInt();
    turnLimit = sc.nextInt();
    level = new String[width][height];
  }

  void parseToplists(Scanner sc) {
    List<Integer> decades = new ArrayList<Integer>();
    // Top tracks
    int numTracks = sc.nextInt();
    sc.nextLine();
    for (int i = 0; i < numTracks; i++) {
      String entry = sc.nextLine();
      // 0:[track],1:[album],2:[artist],3:[year]
      String[] parts = entry.split(",");
      topTracks.add(parts[0]);
      int decade = Util.toDecade(Integer.parseInt(parts[3]));
      decades.add(decade);
    }
    // Top albums
    int numAlbums = sc.nextInt();
    sc.nextLine();
    for (int i = 0; i < numAlbums; i++) {
      String entry = sc.nextLine();
      // 0:[album],1:[artist],2:[year]
      String[] parts = entry.split(",");
      topAlbums.add(parts[0]);
      int decade = Util.toDecade(Integer.parseInt(parts[2]));
      decades.add(decade);
    }
    // Top decade
    topDecade = Util.getPopularElement(
        decades.toArray(new Integer[decades.size()]));
    // Top artists
    int numArtists = sc.nextInt();
    sc.nextLine();
    for (int i = 0; i < numArtists; i++) {
      // 0:[artist]
      String entry = sc.nextLine();
      topArtists.add(entry);
    }
    // Disliked artists
    int numDislikedArtists = sc.nextInt();
    sc.nextLine();
    for (int i = 0; i < numDislikedArtists; i++) {
      // 0:[artist]
      String entry = sc.nextLine();
      dislikedArtists.add(entry);
    }
  }

  void parseTurn(Scanner sc) {
    turn = sc.nextInt();
    remainingCapacity = sc.nextInt();
    remainingExecutionTime = sc.nextInt();
    boostCooldown = sc.nextInt();
  }

  void parseMetadata(Scanner sc) {
    int numResults = sc.nextInt();
    sc.nextLine();
    for (int i = 0; i < numResults; i++) {
      String metadata = sc.nextLine();
      Track knownTrack = Track.fromMetadata(metadata);
      // 0:[uri],1:[[track],[album],[artist],[year]]      
      String[] parts = metadata.split(",", 1);
      
      if(dislikedArtists.contains(knownTrack.artist)) {
          knownTrack.value = -16;
      } else if(topTracks.contains(knownTrack.name)) {
          knownTrack.value = -4;
      } else {
          int tier = 0;
          
          if(topArtists.contains(knownTrack.artist)) {
              tier++;
          }
          if(topAlbums.contains(knownTrack.album)) {
              tier++;
          }
          if(topDecade == Util.toDecade(Integer.parseInt(knownTrack.year))) {
              tier++;
          }
          
          switch(tier) {
              case 1:   knownTrack.value = 4;
                        break;
              case 2:   knownTrack.value = 16;
                        break;
              case 3:   knownTrack.value = 64;
                        break;
          }
      }
          
      knownURIs.put(parts[0], knownTrack);
      foundTrack = knownTrack.value > 0 ? knownTrack : null;
    }
  }

  void parseLevel(Scanner sc) {
    unknownTracks = new ArrayList<Track>();
    knownTracks = new ArrayList<Track>();
    for (int y = 0; y < height; y++) {
      String row = sc.nextLine();
      String[] cells = row.split(",");
      for (int x = 0; x < width; x++) {
        level[x][y] = cells[x].equals("_") ? " " : cells[x];
        if (Util.isURI(cells[x])) {
          String uri = cells[x];
          if (!knownURIs.containsKey(uri)) {
            unknownTracks.add(new Track(uri).place(x, y));
          } else {
            knownTracks.add(knownURIs.get(uri).copy().place(x, y)); 
          }
        }
      }
    }
  }
  
  void writeToCache() throws Exception {
    ObjectOutputStream out = 
      new ObjectOutputStream(new FileOutputStream(CACHE_PREFIX + id));
    out.writeObject(this);
    out.close();
  }

  static Monkey readFromCache(String id) throws Exception {
    ObjectInputStream in =
      new ObjectInputStream(new FileInputStream(CACHE_PREFIX + id));
    Monkey monkey = (Monkey) in.readObject();
    in.close();
    return monkey;
  }

}